package com.base.service.workflow.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.base.core.context.utils.SpringUtils;
import com.base.core.framework.sql.service.impl.SqlBaseServiceImplV1Ext;
import com.base.core.head.constants.CodeResVal;
import com.base.service.workflow.bo.AssignmentStatusEnum;
import com.base.service.workflow.bo.ProcessStatusEnum;
import com.base.service.workflow.dao.AssignmentDao;
import com.base.service.workflow.entity.AssignmentDO;
import com.base.service.workflow.entity.FlowDO;
import com.base.service.workflow.entity.ProcessDO;
import com.base.service.workflow.entity.TaskDO;
import com.base.service.workflow.manager.ProcessDefineService;
import com.base.service.workflow.service.AssignmentService;
import com.base.service.workflow.service.FlowService;
import com.base.service.workflow.service.ProcessService;
import com.base.service.workflow.service.TaskService;
import com.gitee.magic.core.json.JsonObject;
import com.gitee.magic.framework.head.exception.BusinessException;
import com.gitee.magic.jdbc.persistence.source.jdbc.sqlplus.conditions.query.QueryWrapper;
import com.gitee.magic.jdbc.persistence.source.jdbc.sqlplus.conditions.update.UpdateWrapper;

/**
 * @author start
 * @date 2022-03-17 09:37:26
 */
@Service("assignmentService")
public class AssignmentServiceImpl extends SqlBaseServiceImplV1Ext<AssignmentDO, Long> implements AssignmentService {

	private final AssignmentDao assignmentDao;
	@Autowired
	private TaskService taskService;
	@Autowired
	private FlowService flowService;
	@Autowired
	private ProcessService processService;

	public AssignmentServiceImpl(@Qualifier("assignmentDao") AssignmentDao assignmentDao) {
		super(assignmentDao);
		this.assignmentDao = assignmentDao;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void execute(
			Boolean isAgree,
			Long assignmentId,
			JsonObject params,
			List<Long> userIds,String description) {
		AssignmentDO assignment = load(assignmentId);
		if(assignment.getStatus()!=AssignmentStatusEnum.PENDING) {
			throw new BusinessException(CodeResVal.CODE_10061);
		}
		FlowDO currentFlow = flowService.load(assignment.getFlowId());
		if (currentFlow.getFinished()!=0L) {
			throw new BusinessException(CodeResVal.CODE_10062);
		}
		ProcessDO process=processService.load(currentFlow.getProcessId());
		if(process.getStatus()!=ProcessStatusEnum.PENDING) {
			throw new BusinessException(CodeResVal.CODE_10063);
		}
		ProcessDefineService handle=(ProcessDefineService)SpringUtils.getBean(process.getBean());
		processed(assignmentId, isAgree?AssignmentStatusEnum.AGREE:AssignmentStatusEnum.REFUSE, description);
		if (isAgree) {
			TaskDO currentTask = taskService.load(currentFlow.getTaskId());
			handle(process,handle,assignment.getUserId(),currentTask,currentFlow,params,userIds);
		}else{
			flowService.setFinished(currentFlow.getId());
			processService.setStatus(currentFlow.getProcessId(), ProcessStatusEnum.REJECT);
			handle.execute(assignment.getUserId(),currentFlow.getProcessId(),ProcessStatusEnum.REJECT);
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void nextExecute(
			ProcessDO process,
			ProcessDefineService handle,
			Long userId,
			TaskDO currentTask,
			FlowDO currentFlow, 
			List<Long> userIds,
			JsonObject params) {
		
		if (currentFlow == null) {
			processService.setStatus(process.getId(), ProcessStatusEnum.ADOPT);
			handle.execute(userId,process.getId(),ProcessStatusEnum.ADOPT);
			return;
		}

		if (currentTask.getData()!=null) {
			if (!currentTask.getData().getBringIn()) {
				userIds.clear();
				userIds.addAll(handle.queryAssigneeUserId(currentTask.getData().getAssignees()));
			}
		}
		List<AssignmentDO> assignmentList = new ArrayList<>();
		for (Long targetUserId : userIds) {
			//默认跳过自己
			if(process.getUserId().equals(targetUserId)) {
				continue;
			}
			AssignmentDO nextAssignment = new AssignmentDO();
			nextAssignment.setFlowId(currentFlow.getId());
			nextAssignment.setProcessId(currentFlow.getProcessId());
			nextAssignment.setUserId(targetUserId);
			nextAssignment.setStatus(AssignmentStatusEnum.PENDING);
			assignmentList.add(nextAssignment);
		}
		if (CollectionUtils.isEmpty(assignmentList)) {
//			flowService.setFinished(currentFlow.getId());
//			TaskDO nextTask = taskService.nextTask(currentTask.getId(), params);
//			FlowDO nextFlow = flowService.nextFlow(currentFlow.getProcessId(),nextTask, params);
//			nextExecute(process,handle,userId,nextTask,nextFlow,userIds,params);
//			return;
			//TODO:无人员的场景:可直接通过、默认指向特别人、异常中止
			throw new BusinessException(CodeResVal.CODE_10064,currentTask.getName());
		}
		saveBatch(assignmentList);
		
		List<AssignmentDO> assList=assignmentList.stream().
				filter(t->t.getStatus().equals(AssignmentStatusEnum.AGREE_AUTO)).toList();
		for(AssignmentDO ass:assList) {
			if(handle(process,handle,ass.getUserId(),currentTask,currentFlow,params,new ArrayList<>())) {
				break;
			}
		}
	}
	
	private boolean handle(
			ProcessDO process,
			ProcessDefineService handle,
			Long userId,
			TaskDO currentTask,
			FlowDO currentFlow,JsonObject params,List<Long> userIds) {
		Boolean isAodpt = true;
		if (currentTask.getData()!=null) {
			if (!currentTask.getData().getOrSign()) {
				QueryWrapper wrapper = new QueryWrapper();
				wrapper.select("IFNULL(COUNT(1),0)");
				wrapper.eq("flowId", currentFlow.getId())
				.eq("status", AssignmentStatusEnum.PENDING.toString());
				if (assignmentDao.queryForInt(wrapper) > 0) {
					// 只有全部审批完成才能执行
					isAodpt = false;
				}
			}else {
				//TODO:自动更新状态为通过
			}
		}
		if (isAodpt) {
			flowService.setFinished(currentFlow.getId());
			TaskDO nextTask = taskService.nextTask(currentTask.getId(), params);
			FlowDO nextFlow = flowService.nextFlow(currentFlow.getProcessId(),currentFlow.getId(),nextTask, params);
			nextExecute(process,handle,userId,nextTask,nextFlow,userIds,params);
			return true;
		}else {
			handle.execute(userId,process.getId(),process.getStatus());
			return false;
		}
	}
	
	@Override
	public void processed(Long assignmentId,AssignmentStatusEnum status,String description) {
		UpdateWrapper wrapper=new UpdateWrapper();
		wrapper.eq("id", assignmentId)
		.eq("status", String.valueOf(AssignmentStatusEnum.PENDING))
		.set("status", String.valueOf(status))
		.set("description", description);
		if(assignmentDao.executeUpdate(wrapper)!=1) {
			throw new BusinessException(CodeResVal.CODE_10005);
		}
	}
	
}
